/**
 * Copyright (c) 2012-2013, Michael Yang 杨福海 (www.yangfuhai.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pw.orm;


import pw.orm.core.Record;
import pw.orm.core.SqlBuilder;
import pw.orm.core.SqlInfo;
import pw.orm.core.TableInfo;
import pw.orm.db.DB;
import pw.orm.db.JormConfig;
import pw.orm.exception.JormException;
import pw.orm.utils.RsUtils;
import pw.orm.utils.StrUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;

/**
 * @author Eric
 */
public class Jorm {

	private static final String TAG = "Jorm";

	private static HashMap<String, Jorm> daoMap = new HashMap<String, Jorm>();

	private JormConfig config;

	private Jorm(JormConfig config) {
		if (config == null){
			throw new JormException("jormConfig is null");
		}
		this.config = config;
	}

	public  static Jorm getInstance(JormConfig jormConfig) {
		Jorm dao = daoMap.get(jormConfig.getConfigName());
		if (dao == null) {
			dao = new Jorm(jormConfig);
			daoMap.put(jormConfig.getConfigName(), dao);
		}
		return dao;
	}


	/**
	 * 保存数据库，速度要比save快
	 * 
	 * @param entity
	 */
	public int save(Object entity) throws SQLException{
		return exeSqlInfo(SqlBuilder.buildInsertSql(entity));
	}

	/**
	 * 先检查后保存，速度要比save慢
	 *
	 * @param entity
	 */
	public void saveOrUpdate(Object entity) throws SQLException{
		Object idValue = SqlBuilder.getIdValue(entity);
		Object obj = findById(idValue,entity.getClass());
		if(obj == null){
			save(entity);
		}else{
			update(entity);
		}
	}

	/**
	 * 保存数据到数据库<br />
	 * <b>注意：</b><br />
	 * 保存成功后，entity的主键将被赋值（或更新）为数据库的主键， 只针对自增长的id有效
	 * @param entity
	 *            要保存的数据
	 * @return ture： 保存成功 false:保存失败
	 */
	public boolean saveBindId(Object entity) throws SQLException{
//		if(!SqlBuilder.isSupportBindId(entity)) return false; // 暂时不做过滤
		Object id = saveSqlInfo(SqlBuilder.buildInsertSql(entity));
		if(id != null){
			TableInfo tf = TableInfo.get(entity.getClass());
			tf.getId().setValue(entity, id);
		}
		return true;
	}

	/**
	 * 更新数据 （主键ID必须不能为空）
	 * 
	 * @param entity
	 */
	public int update(Object entity) throws SQLException{
		return exeSqlInfo(SqlBuilder.getUpdateSqlAsSqlInfo(entity));
	}

	/**
	 * 根据条件更新数据
	 * 
	 * @param entity
	 * @param strWhere
	 *            条件为空的时候，将会更新所有的数据
	 */
	public int update(Object entity, String strWhere) throws SQLException{
		return exeSqlInfo(SqlBuilder.getUpdateSqlAsSqlInfo(entity, strWhere));
	}

	/**
	 * 删除数据
	 * 
	 * @param entity
	 *            entity的主键不能为空
	 */
	public int delete(Object entity) throws SQLException{
		return exeSqlInfo(SqlBuilder.buildDeleteSql(entity));
	}

	/**
	 * 根据主键删除数据
	 * 
	 * @param clazz
	 *            要删除的实体类
	 * @param id
	 *            主键值
	 */
	public int deleteById(Class<?> clazz, Object id) throws SQLException{
		return exeSqlInfo(SqlBuilder.buildDeleteSql(clazz, id));
	}

	/**
	 * 根据条件删除数据
	 * 
	 * @param clazz
	 * @param strWhere
	 *            条件为空的时候 将会删除所有的数据
	 */
	public int deleteByWhere(Class<?> clazz, String strWhere) throws SQLException {
		String sql = SqlBuilder.buildDeleteSql(clazz, strWhere);
		return exeSqlInfo(sql);
	}

	/**
	 * 删除表的所有数据
	 * 
	 * @param clazz
	 */
	public void deleteAll(Class<?> clazz) throws SQLException{
		String sql = SqlBuilder.buildDeleteSql(clazz, null);
		exeSqlInfo(sql);
	}

	private Object saveSqlInfo(SqlInfo sqlInfo) throws SQLException{
		if (sqlInfo != null) {
			debugSql(sqlInfo.getSql());
			Object result = null;
			try{
				Connection connection = config.getConnection();
				result = DB.save(connection,sqlInfo.getSql(), sqlInfo.getBindArgsAsArray());
				config.close(connection);
			}catch (Exception e){
				e.printStackTrace();
			}
			return result;
		} else {
			config.getLog().e(TAG, "sava error:sqlInfo is null");
		}

		return null;
	}
	private int exeSqlInfo(SqlInfo sqlInfo) throws SQLException{
		int result = -1;
		if (sqlInfo != null) {
			debugSql(sqlInfo.getSql());
			try{
				Connection connection = config.getConnection();
				result = DB.update(connection,sqlInfo.getSql(), sqlInfo.getBindArgsAsArray());
				config.close(connection);
			}catch (Exception e){
				e.printStackTrace();
			}
		} else {
			config.getLog().e(TAG, "sava error:sqlInfo is null");
		}
		return result;
	}

	private int exeSqlInfo(String sql) throws SQLException{
		int result = -1;
		if (!StrUtils.isEmpty(sql)) {
			debugSql(sql);
			try{
				Connection connection = config.getConnection();
				result = DB.update(connection,sql, null);
				config.close(connection);
			}catch (Exception e){
				e.printStackTrace();
			}
		} else {
			config.getLog().e(TAG, "sava error:sql is null");
		}
		return result;
	}

	/**
	 * 用于执行原生的sql
	 * @param sql
	 */
	public void exeRawSql(String sql){
		try{
			debugSql(sql);

			Connection connection = config.getConnection();
			Statement stat = connection.createStatement();
			stat.execute(sql);
			stat.close();
			config.close(connection);
		}catch (Exception e ){
			e.printStackTrace();
		}
	}

	private <T> List<T> find(Class<T> clazz ,SqlInfo sqlInfo){
		try{
			debugSql(sqlInfo.getSql());

			Connection connection = config.getConnection();
			ResultSet rs = DB.select(connection,sqlInfo.getSql(),sqlInfo.getBindArgsAsArray());
			List<T> list = RsUtils.getEntities(rs,clazz);
			config.close(connection);
			return list;
		}catch (Exception e){
			e.printStackTrace();
		}
		return null;
	}

	private <T> List<T>  find(Class<T> clazz ,String sql){
		try{
			debugSql(sql);

			Connection connection = config.getConnection();
			ResultSet rs = DB.select(connection,sql,null);
			List<T> list = RsUtils.getEntities(rs,clazz);
			config.close(connection);
			return list;
		}catch (Exception e){
			e.printStackTrace();
		}
		return null;
	}

	private List<Record> find(String sql){
		try{
			debugSql(sql);

			Connection connection = config.getConnection();
			ResultSet rs = DB.select(connection,sql,null);
			List<Record> list = RsUtils.getRecord(rs);
			config.close(connection);
			return list;
		}catch (Exception e){
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 根据主键查找数据（默认不查询多对一或者一对多的关联数据）
	 * 
	 * @param id
	 * @param clazz
	 */
	public <T> T findById(Object id, Class<T> clazz) {
		SqlInfo sqlInfo = SqlBuilder.getSelectSqlAsSqlInfo(clazz, id);

		debugSql(sqlInfo.getSql());

		List<T> list = find(clazz,sqlInfo);
		return list != null && list.size() > 0 ? list.get(0):null;
	}

	/**
	 * 查找所有的数据
	 * 
	 * @param clazz
	 */
	public <T> List<T> findAll(Class<T> clazz) {
		return findAllBySql(clazz, SqlBuilder.getSelectSQL(clazz));
	}

	/**
	 * 查找所有数据
	 * 
	 * @param clazz
	 * @param orderBy
	 *            排序的字段
	 */
	public <T> List<T> findAll(Class<T> clazz, String orderBy) {
		return findAllBySql(clazz, SqlBuilder.getSelectSQL(clazz)
				+ " ORDER BY " + orderBy);
	}

	/**
	 * 根据条件查找所有数据
	 * 
	 * @param clazz
	 * @param strWhere
	 *            条件为空的时候查找所有数据
	 */
	public <T> List<T> findAllByWhere(Class<T> clazz, String strWhere) {
		return findAllBySql(clazz,
				SqlBuilder.getSelectSQLByWhere(clazz, strWhere));
	}

	/**
	 * 根据条件查找所有数据
	 * 
	 * @param clazz
	 * @param strWhere
	 *            条件为空的时候查找所有数据
	 * @param orderBy
	 *            排序字段
	 */
	public <T> List<T> findAllByWhere(Class<T> clazz, String strWhere,
			String orderBy) {
		return findAllBySql(clazz,
				SqlBuilder.getSelectSQLByWhere(clazz, strWhere) + " ORDER BY "
						+ orderBy);
	}

	/**
	 * 根据条件查找所有数据
	 * 
	 * @param clazz
	 * @param strSQL
	 */
	private <T> List<T> findAllBySql(Class<T> clazz, String strSQL) {
		return find(clazz,strSQL);
	}

	/**
	 * 根据sql语句查找数据，这个一般用于数据统计
	 * 
	 * @param strSQL
	 */
	public Record findRecordBySQL(String strSQL) {
		List<Record> records = find(strSQL);
		return records != null && records.size()>0 ? records.get(0):null;
	}

	public List<Record> findRecordListBySQL(String strSQL) {
		return find(strSQL);
	}

	private void debugSql(String sql) {
		if (config != null && config.isDebug()){
			config.getLog().d("Jorm"," Debug SQL >> " + sql);
		}
	}


}
